package com.xhtt.modules.inspection.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xhtt.common.exception.RRException;
import com.xhtt.common.exception.RRExceptionEnum;
import com.xhtt.common.utils.*;
import com.xhtt.modules.inspection.dao.InsPointMapper;
import com.xhtt.modules.inspection.dto.InsPointExportDTO;
import com.xhtt.modules.inspection.entity.InsLabel;
import com.xhtt.modules.inspection.entity.InsPoint;
import com.xhtt.modules.inspection.entity.InsPointLabel;
import com.xhtt.modules.inspection.entity.InsPointSubitem;
import com.xhtt.modules.inspection.enums.PointDeviceTypeEnum;
import com.xhtt.modules.inspection.enums.PointSubTypeEnum;
import com.xhtt.modules.inspection.enums.PointTypeEnum;
import com.xhtt.modules.inspection.service.IInsLabelService;
import com.xhtt.modules.inspection.service.IInsPointLabelService;
import com.xhtt.modules.inspection.service.IInsPointService;
import com.xhtt.modules.inspection.service.IInsPointSubitemService;
import com.xhtt.modules.inspection.vo.InsPointVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 巡检点 服务实现类
 * </p>
 *
 * @author xj
 * @since 2019-01-18
 */
@Service
public class InsPointServiceImpl extends ServiceImpl<InsPointMapper, InsPoint> implements IInsPointService {

    @Autowired
    private IInsPointLabelService insPointLabelService;

    @Autowired
    private IInsPointSubitemService insPointSubService;

    @Autowired
    private IInsLabelService insLabelService;

    @Autowired
    private IInsPointSubitemService insPointSubitemService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        Page<InsPoint> page = new Query<InsPoint>(params).getPage();
        if (params.get("hidePointIds") != null){
            String hidePointIds = params.get("hidePointIds").toString();
            List<String> hidePointIdsList = Arrays.asList(hidePointIds.split(","));
            params.put("hidePointIds", hidePointIdsList);
        }
        page.setRecords(baseMapper.queryPage(page, params));
//        IPage<InsPoint> iPage = baseMapper.selectPage(page, new LambdaQueryWrapper<InsPoint>()
////                .like(Optional.ofNullable(params.get("name")).isPresent(), InsPoint::getName, params.get("name"))
//                .eq(Optional.ofNullable(params.get("pointType")).isPresent(), InsPoint::getPointType, params.get("pointType"))
//                .like(Optional.ofNullable(params.get("deviceName")).isPresent(), InsPoint::getDeviceName, params.get("deviceName"))
//                .eq(Optional.ofNullable(params.get("deviceType")).isPresent(), InsPoint::getDeviceType, params.get("deviceType"))
//                .orderByDesc(InsPoint::getUpdateTime)
//                .notInSql(Optional.ofNullable(params.get("hidePointIds")).isPresent(), InsPoint::getId, (String) params.get("hidePointIds")));
//
//        List<InsPoint> insPointList = iPage.getRecords();
//        if (CollectionUtils.isNotEmpty(insPointList)) {
//            for (InsPoint insPoint : insPointList) {
//                // 设置绑定标签
//                String labelName = insPointLabelService.getLabelNameByPointId(insPoint.getId());
//                insPoint.setLabelName(labelName);
//
//                // 设置巡检内容
//                List<InsPointSubitem> insPointSubList = insPointSubService.getInsPointSubByPointId(insPoint.getId());
//                if (CollectionUtils.isNotEmpty(insPointSubList)) {
//                    List<String> checkNames = insPointSubList.stream().map(InsPointSubitem::getName)
//                            .collect(Collectors.toList());
//                    insPoint.setCheckNameList(checkNames);
//                }
//            }
//        }
        return new PageUtils(page);
    }

    @Override
    @Transactional
    public void add(InsPointVO insPointVo) {

        List<InsPointSubitem> insPointSubs = insPointVo.getInsPointSubList();

        // 判断检点标准值格式
        insPointSubs.forEach(pointSub -> {
            if (pointSub.getType().equals(PointSubTypeEnum.STR.getCode())) {
                pointSub.setValue(pointSub.getValue().replace("；", ";"));
            }
        });

        // 创建检点对象
        InsPoint insPoint = new InsPoint();
        BeanUtils.copyProperties(insPointVo, insPoint);
        // 如果检点类别选择的是检查表,则清空设备类型专属字段
        if (insPoint.getPointType().equals(PointTypeEnum.JIAN_CHA_BIAO.getCode())) {
            insPoint.setDeviceType(null);
        }
        // 判断设备名称/检查项唯一性
        int deviceNameExist = baseMapper
                .selectCount(new LambdaQueryWrapper<InsPoint>()
                        .eq(InsPoint::getDeviceName, insPointVo.getDeviceName()));
        if (deviceNameExist > 0) {
            throw new RRException(RRExceptionEnum.POINT_DEVICE_NAME_EXIST);
        }
        // 保存检点
        baseMapper.insert(insPoint);

        // 创建检点与标签关联表
        InsPointLabel insPointLabel = new InsPointLabel();
        insPointLabel.setPointId(insPoint.getId());
        insPointLabel.setLabelId(insPointVo.getLabelId());
        // 保存关联表对象
        insPointLabelService.save(insPointLabel);

        // 创建检点子表
        List<InsPointSubitem> insPointSubList = insPointVo.getInsPointSubList();
        for (InsPointSubitem insPointSub : insPointSubList) {
            insPointSub.setPointId(insPoint.getId());
            // 数值时清空状态专属字段
            if (insPointSub.getType().equals(PointSubTypeEnum.NUM.getCode())) {
                // TODO 清空"状态"专属字段 暂时没有
            } else if (insPointSub.getType().equals(PointSubTypeEnum.STR.getCode())) { // 状态清空数值专属字段
                insPointSub.setMaxValue(null);
                insPointSub.setMinValue(null);
                insPointSub.setUnit(null);
            }
        }

        // 保存检点子表
        insPointSubService.saveBatch(insPointSubList);
    }

    @Override
    public InsPointVO detail(Integer pointId) {

        // 构造返回对象
        InsPointVO insPointVo = new InsPointVO();

        // 主表
        InsPoint insPoint = baseMapper.selectById(pointId);
        BeanUtils.copyProperties(insPoint, insPointVo);

        // 设置标签名称
        String labelName = insPointLabelService.getLabelNameByPointId(pointId);

        // 添加标签id,编辑时用到
        InsPointLabel insPointLabel = insPointLabelService
                .getOne(new LambdaQueryWrapper<InsPointLabel>().eq(InsPointLabel::getPointId, pointId));
        insPointVo.setLabelId(insPointLabel.getLabelId());
        insPointVo.setLabelName(labelName);

        // 子表
        List<InsPointSubitem> insPointSubList = insPointSubService
                .list(new LambdaQueryWrapper<InsPointSubitem>()
                        .eq(InsPointSubitem::getPointId, pointId));
        insPointVo.setInsPointSubList(insPointSubList);

        return insPointVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(InsPointVO insPointVo) {

        // 检测点保存
        InsPoint insPoint = new InsPoint();
        BeanUtils.copyProperties(insPointVo, insPoint);

        // 判断设备名称/检查项唯一性
        int deviceNameExist = baseMapper
                .selectCount(new LambdaQueryWrapper<InsPoint>()
                        .eq(InsPoint::getDeviceName, insPointVo.getDeviceName())
                        .ne(InsPoint::getId, insPointVo.getId()));
        if (deviceNameExist > 0) {
            throw new RRException(RRExceptionEnum.POINT_DEVICE_NAME_EXIST);
        }

        baseMapper.updateById(insPoint);

        // 根据检点id查询检点和标签关联表数据
        Integer insPointId = insPointVo.getId();
        InsPointLabel insPointLabel = insPointLabelService
                .getOne(new LambdaQueryWrapper<InsPointLabel>()
                        .eq(InsPointLabel::getPointId, insPointId));

        // 如果之前没有关联就创建,创建时已经在页面验证了,一般不会出现
        if (insPointLabel == null) {
            insPointLabel = new InsPointLabel();
            insPointLabel.setPointId(insPointId);
            insPointLabel.setLabelId(insPointVo.getLabelId());
            insPointLabelService.save(insPointLabel);
        } else {
            insPointLabel.setLabelId(insPointVo.getLabelId());
            // 保存关联表
            insPointLabelService.updateById(insPointLabel);
        }

        // 删除所有子表
        insPointSubService.remove(new LambdaQueryWrapper<InsPointSubitem>().eq(InsPointSubitem::getPointId, insPointId));

        // 保存子表
        List<InsPointSubitem> insPointSubList = insPointVo.getInsPointSubList();

        /*
         * 设置外键,防止错误数据修改时不能正常保存
         * 正常情况下是不需要设置的
         */
        insPointSubList.forEach(e -> e.setPointId(insPointVo.getId()));
        insPointSubService.saveOrUpdateBatch(insPointSubList);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Integer> ids) {

        // 删除检测点表的数据
        baseMapper.deleteBatchIds(ids);

        // 删除检点和标签关联表记录
        insPointLabelService.remove(new LambdaQueryWrapper<InsPointLabel>().in(InsPointLabel::getPointId, ids));

        // 删除巡检内容表的数据
        insPointSubService.remove(new LambdaQueryWrapper<InsPointSubitem>()
                .in(InsPointSubitem::getPointId, ids));

    }

    @Override
    public InsPoint getAllById(Integer pointId) {
        return baseMapper.queryById(pointId);
    }

    @Override
    @Transactional
    public void upload(Map<Long, Map<Integer, String>> params) {
//        Long headerRowNum = 2L;
//        // 初始化下标（用于判断头部字段是否存在,以及对应关系）
//        int pointTypeIndex = -1;
//        int deviceNameIndex = -1;
//        int pidNumIndex = -1;
//        int deviceTypeIndex = -1;
//        int labelNameIndex = -1;
//        int pointContentIndex = -1;
//        int pointContentTypeIndex = -1;
//        int valueIndex = -1;
//        int maxIndex = -1;
//        int minIndex = -1;
//        int unitIndex = -1;
//        int photoIndex = -1;
//        // 获取头部一行数数据
//        Map<Integer, String> header = params.get(headerRowNum);
//        for (int headerIndex : header.keySet()) {
//            String f = header.get(headerIndex).trim();
//            switch (f) {
//                case InsPoint.POINT_TYPE: {
//                    pointTypeIndex = headerIndex;
//                    break;
//                }
//                case InsPoint.DEVICE_NAME:
//                    deviceNameIndex = headerIndex;
//                    break;
//                case InsPoint.PID_NUM:
//                    pidNumIndex = headerIndex;
//                    break;
//                case InsPoint.DEVICE_TYPE:
//                    deviceTypeIndex = headerIndex;
//                    break;
//                case InsPoint.LABEL_NAME:
//                    labelNameIndex = headerIndex;
//                    break;
//                case InsPoint.POINT_CONTENT:
//                    pointContentIndex = headerIndex;
//                    break;
//                case InsPoint.POINT_CONTENT_TYPE:
//                    pointContentTypeIndex = headerIndex;
//                    break;
//                case InsPoint.VALUE:
//                    valueIndex = headerIndex;
//                    break;
//                case InsPoint.MAX:
//                    maxIndex = headerIndex;
//                    break;
//                case InsPoint.MIN:
//                    minIndex = headerIndex;
//                    break;
//                case InsPoint.UNIT:
//                    unitIndex = headerIndex;
//                    break;
//                case InsPoint.PHOTO:
//                    photoIndex = headerIndex;
//            }
//        }
//        List<Integer> pointTypeEmptyIndex = new ArrayList<>();
//        List<Integer> deviceNameEmptyIndex = new ArrayList<>();
//        List<Integer> labelNameEmptyIndex = new ArrayList<>();
//        List<Integer> pointContentEmptyIndex = new ArrayList<>();
//        List<Integer> pointContentTypeEmptyIndex = new ArrayList<>();
//        List<Integer> photoEmptyIndex = new ArrayList<>();
//        List<Integer> labelNameExistIndex = new ArrayList<>();
//        List<Integer> valueEmptyIndex = new ArrayList<>();
//        List<Integer> maxEmptyIndex = new ArrayList<>();
//        List<Integer> minEmptyIndex = new ArrayList<>();
//        List<Integer> unitEmptyIndex = new ArrayList<>();

        Set<Integer> hasErrorLines = new HashSet<>();

        params.forEach((k, v) -> {
            InsPointVO insPointVO = new InsPointVO();
            InsPointSubitem insPointSubitem = new InsPointSubitem();

            // 判断是否允许添加
            boolean hasError = false;
            if (k > 2L) {
                try {
                    String pointType = v.get(0);
                    if (StringUtils.isEmpty(pointType)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointVO.setPointType(pointType.equals(PointTypeEnum.JIAN_CHA_BIAO.getMsg()) ? PointTypeEnum.JIAN_CHA_BIAO.getCode() : PointTypeEnum.SHE_BEI.getCode());
                    }

                    String deviceName = v.get(1);
                    if (StringUtils.isEmpty(deviceName)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointVO.setDeviceName(deviceName);
                    }

                    String pidNum = v.get(2);
                    if (StringUtils.isNotEmpty(pidNum)) {
                        insPointVO.setName(pidNum);
                    }

                    String deviceType = v.get(3);
                    if (StringUtils.isNotEmpty(deviceType)) {
                        if (deviceType.equals("常用")) insPointVO.setDeviceType(1);
                        else insPointVO.setDeviceType(2);
                    }

                    String labelName = v.get(4);
                    if (StringUtils.isEmpty(labelName)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        InsLabel insLabel = insLabelService.getOne(new LambdaQueryWrapper<InsLabel>().eq(InsLabel::getName, labelName));
                        if (insLabel == null) {
//                            hasErrorLines.add(k.intValue());
                            hasError = true;
                        } else {
                            insPointVO.setLabelId(insLabel.getId());
                        }
                    }

                    String pointContent = v.get(5);
                    if (StringUtils.isEmpty(pointContent)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointSubitem.setName(pointContent);
                    }

                    String pointContentType = v.get(6);
                    if (StringUtils.isEmpty(pointContentType)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointSubitem.setType(pointContentType.equals(PointSubTypeEnum.NUM.getMsg()) ? PointSubTypeEnum.NUM.getCode() : PointSubTypeEnum.STR.getCode());
                    }

                    String value = v.get(7);
                    if (pointContentType.equals(PointSubTypeEnum.STR.getMsg()) && StringUtils.isEmpty(value)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointSubitem.setValue(value);
                    }

                    String max = v.get(8);
                    String min = v.get(9);
                    String unit = v.get(10);
                    if (pointContentType.equals(PointSubTypeEnum.NUM.getMsg())) {
                        if (StringUtils.isNotEmpty(max)) {
                            insPointSubitem.setMaxValue(Integer.valueOf(max));
                        }
                        if (StringUtils.isNotEmpty(min)) {
                            insPointSubitem.setMinValue(Integer.valueOf(min));
                        }
                        if (StringUtils.isEmpty(unit)) {
//                            hasErrorLines.add(k.intValue());
                            hasError = true;
                        } else {
                            insPointSubitem.setUnit(unit);
                        }
                    }

                    String photo = v.get(11);
                    if (StringUtils.isEmpty(photo)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointSubitem.setIsPhoto(photo.equals("是"));
                    }
                    String require = v.get(12);
                    if (StringUtils.isEmpty(require)) {
//                        hasErrorLines.add(k.intValue());
                        hasError = true;
                    } else {
                        insPointSubitem.setIsPhoto(photo.equals("需要"));
                    }

                    if (hasError) {
                        hasErrorLines.add(k.intValue());
                    } else {
                        insPointVO.setInsPointSubList(Arrays.asList(insPointSubitem));
                        try {
                            this.add(insPointVO);
                        } catch (Exception ex) {
                            // 报错代表数据库已存在这个字段,获取这条数据的id并添加检点内容
                            if (ex.getMessage().equals(RRExceptionEnum.POINT_DEVICE_NAME_EXIST.getMsg())) {
                                System.out.println("查询");
                                InsPoint insPoint = this.getOne(new LambdaQueryWrapper<InsPoint>().eq(InsPoint::getDeviceName, insPointVO.getDeviceName()));
                                if (insPoint != null) {
                                    InsPointSubitem insPointSub = insPointVO.getInsPointSubList().get(0);
                                    // 防止反复添加，数据库存在相同的不进行添加操作
                                    int existCount = 0;
                                    if (insPointSub.getType().equals(PointSubTypeEnum.NUM.getCode())) {
                                        existCount = insPointSubService
                                                .count(new LambdaQueryWrapper<InsPointSubitem>()
                                                        .eq(InsPointSubitem::getPointId, insPoint.getId())
                                                        .eq(InsPointSubitem::getName, insPointSub.getName())
                                                        .eq(InsPointSubitem::getType, insPointSub.getType())
                                                        .eq(InsPointSubitem::getIsPhoto, insPointSub.getIsPhoto())
                                                        .eq(InsPointSubitem::getMaxValue, insPointSub.getMaxValue())
                                                        .eq(InsPointSubitem::getMinValue, insPointSub.getMinValue())
                                                        .eq(InsPointSubitem::getUnit, insPointSub.getUnit()));
                                    } else if (insPointSub.getType().equals(PointSubTypeEnum.STR.getCode())) {
                                        existCount = insPointSubService
                                                .count(new LambdaQueryWrapper<InsPointSubitem>()
                                                        .eq(InsPointSubitem::getPointId, insPoint.getId())
                                                        .eq(InsPointSubitem::getName, insPointSub.getName())
                                                        .eq(InsPointSubitem::getIsPhoto, insPointSub.getIsPhoto())
                                                        .eq(InsPointSubitem::getType, insPointSub.getType())
                                                        .eq(InsPointSubitem::getValue, insPointSub.getValue()));
                                    }
                                    if (existCount == 0) {
                                        insPointSub.setPointId(insPoint.getId());
                                        insPointSubService.save(insPointSub);
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    hasErrorLines.add(k.intValue());
                }
            }
        });
        if (CollectionUtils.isNotEmpty(hasErrorLines)) {
            throw new RRException(insLabelService.formatUploadEx("第", hasErrorLines, "行有错误").toString());
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void exportExcel(HttpServletResponse response, Map<String, Object> params) {
        ExportExcel exportExcel = new ExportExcel();

        params.put("limit", "65000");

        PageUtils pageUtils = queryPage(params);
        List<InsPoint> list = (List<InsPoint>) pageUtils.getList();

        // 待添加集合
        List<InsPointExportDTO> result = new ArrayList<>();
        InsPointExportDTO insPointExportDTO;
        for (InsPoint insPoint : list) {

            // 巡检内容
            List<InsPointSubitem> pointSubs = insPointSubitemService.list(new LambdaQueryWrapper<InsPointSubitem>().eq(InsPointSubitem::getPointId, insPoint.getId()));

            // 一对多,几个内容就是几条数据
            for (InsPointSubitem pointSub : pointSubs) {

                // 设置检点基础数据
                insPointExportDTO = new InsPointExportDTO();
                BeanUtils.copyProperties(pointSub, insPointExportDTO);

                // 属性需要copy之后再设置
                insPointExportDTO.setPointType(EnumUtils.getMsgByCode(PointTypeEnum.class, insPoint.getPointType()).getMsg());
                insPointExportDTO.setDeviceName(insPoint.getDeviceName());
                insPointExportDTO.setDeviceType(EnumUtils.getMsgByCode(PointDeviceTypeEnum.class, insPoint.getDeviceType()).getMsg());
                insPointExportDTO.setLabelName(insPoint.getLabelName());

                insPointExportDTO.setIsPhone(pointSub.getIsPhoto() ? "是" : "否");
                insPointExportDTO.setRequire(insPoint.getRequireFlag() ? "需要" : "不需要");
                result.add(insPointExportDTO);
            }
        }

        String fileName = "检点数据";
        String sheetName = "sheet1";
        String[] headers = {"检点类别", "设备/检查项名称", "设备类别", "绑定标签", "巡检内容", "状态", "最大值", "最小值", "单位", "是否需要拍照", "需要巡检操作"};
        String[] columns = {"pointType", "deviceName", "deviceType", "labelName", "name", "value", "maxValue", "minValue", "unit", "isPhone", "require"};
        try {
            exportExcel.exportExcel(fileName, sheetName, headers, columns, result, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RRException(RRExceptionEnum.EXPORT_FAIL);
        }
    }
}
